Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix missing module in large app from Experimental Bundler #8303

Merged
merged 12 commits into from Jul 27, 2022

Conversation

AGawrys
Copy link
Contributor

@AGawrys AGawrys commented Jul 12, 2022

This PR addresses a missing module where only shared bundles were considered when attempting to remove a shared bundle from a group.

When determining what source bundles need the shared-bundle-to-remove's assets pushed back into it. Instead of consulting the total BundleGroup bundles, we consulted only shared bundles. This structure would then always be empty, and so shared bundles were removed from groups but not placed into source bundles at all.

Major Changes

  • We've removed sharedToSourceBundles (A structure that held shared bundle ids to source bundle ids)
  • We now handle the case where if a shared bundle is removed from a Bundle Group, and only used elsewhere once, it should just be fully removed
  • We now consider reused bundles in parallel request limit removal
  • Refactor reused bundle code (we attempt to reuse bundles before creating a shared bundle if a bundle is the exact subgraph of some other set of bundles)

Reused Bundles

Reused bundles are bundles that are shared (i.e. they have source bundles) but also have a mainEntryAsset. These bundles have a referencing bundle (i.e. something that imports it) but are also exact subgraphs of other bundles. So, they can be shared, but they should also never be removed by parallel request limits since they have a referencing bundles (something that dynamically imported it, causing a new bundle to be created) (See future todo for plans to remove these as an optimization)

Diagrams

In this bundleGraph,

Bar.js requires a.js and b.js (and requires Foo.js through a.js)
Foo.js requires a.js and b.js
a.js requires foo.js

Thus, foo.js is a subgraph of bar.js and creating a shared bundle would be wrong since it would be an exact copy of the foo bundle and leave an empty shell bundle behind.

IdealBundleGraph_Final

✔️ PR Todo

  • Added/updated unit tests for this change
  • Filled out test instructions (In case there aren't any unit tests)
  • Test on large app

Future TODO

  • Remove sourceBundle property from bundles in favor of reading graph
  • Remove a reused bundle if it has been removed by parallel request limit (currently this is not done because reused bundle have some referencing or parent bundle as it's main reason for being a bundle. However, that bundle may be small and better suited to simply be merged back in)
  • [FIXED] Potential error: What if a reused bundle has a shared bundle with some other bundle. Then, if that bundle group hits the parallel request limit, and is merged back in, only the reused bundles' assets would be merged in and the shared bundle is now not connected

Shared Bundle under Reused Bundle Case Diagram

GRAPH

@parcel-benchmark
Copy link

parcel-benchmark commented Jul 12, 2022

Benchmark Results

Kitchen Sink ✅

Timings

Description Time Difference
Cold 1.50s +6.00ms
Cached 344.00ms +7.00ms

Cold Bundles

No bundle changes detected.

Cached Bundles

No bundle changes detected.

React HackerNews ✅

Timings

Description Time Difference
Cold 9.14s +126.00ms
Cached 444.00ms -11.00ms

Cold Bundles

No bundle changes detected.

Cached Bundles

No bundle changes detected.

AtlasKit Editor ✅

Timings

Description Time Difference
Cold 1.61m -4.00ms
Cached 2.50s -45.00ms

Cold Bundles

Bundle Size Difference Time Difference
dist/index.html 240.00b +0.00b 1.21m +33.32s ⚠️

Cached Bundles

Bundle Size Difference Time Difference
dist/esm.c7dc1640.js 61.96kb +0.00b 1.20m +31.79s ⚠️
dist/workerHasher.e50d242f.js 1.72kb +0.00b 1.20m +31.79s ⚠️
dist/16.1969624f.js 1.08kb +0.00b 38.95s +2.40s ⚠️
dist/index.html 240.00b +0.00b 1.20m +36.87s ⚠️

Three.js ✅

Timings

Description Time Difference
Cold 6.67s +82.00ms
Cached 272.00ms -1.00ms

Cold Bundles

No bundle changes detected.

Cached Bundles

No bundle changes detected.

Click here to view a detailed benchmark overview.

Copy link
Member

@devongovett devongovett left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few questions, but lets get this in so we can test

packages/bundlers/experimental/src/ExperimentalBundler.js Outdated Show resolved Hide resolved
invariant(reusableBundle !== 'root' && reusableBundle != null);
reusableBundle.sourceBundles.add(candidateSourceBundleId);
} else {
// Asset is not a bundleRoot, but if its parent bundle can be
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only the direct parent? Is there any case where we'd have to do this recursively?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So actually I think the comment isn't the best. Essentially what we're trying to do here is:

Of the reachable assets for an asset, is there some common subset (under a bundleRoot) that may be reused.
So I guess it's not so much as a parent, as it is any bundleRoot that synchronously imports the asset in question. (which to me is kind of a parent? Just not the only parent)

So I guess the better way to phrase would be "if a bundleRoot of a bundle that this asset is in, can be reused by some other bundleRoot in the asset's reachable, reuse it"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And it's important to note, this code path you're talking about was only added because we can't guarantee that we process a bundleRoot before any non-bundleRoot assets that are in it, when placing assets into bundles.
So like if we process a non-bundleRoot asset, we need to make sure we don't prematurely place it in, where it could've been reused by some other bundleRoot

Copy link
Contributor Author

@AGawrys AGawrys Jul 27, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Diagrams for this section might be important 😄 ..working on it

@AGawrys AGawrys merged commit 70b2abc into v2 Jul 27, 2022
@AGawrys AGawrys deleted the remove-reused-shared-bundle-under-limit branch July 28, 2022 19:37
gorakong pushed a commit that referenced this pull request Nov 3, 2022
* upstream/v2: (22 commits)
  Cross compile toolchains are built into docker image already
  Also fix release build
  Update centos node version
  v2.7.0
  Changelog for v2.7.0
  Use placeholder expression when replacing unused symbols (#8358)
  Lint (#8359)
  Add support for errorRecovery option in @parcel/transformer-css (#8352)
  VS Code Extension for Parcel (#8139)
  Add multi module compilation for elm (#8076)
  Bump terser from 5.7.2 to 5.14.2 (#8322)
  Bump node-forge from 1.2.1 to 1.3.0 (#8271)
  allow cjs config files on type module projects (#8253)
  inject script for hmr when there is only normal script in html (#8330)
  feat: support react refresh for @emotion/react (#8205)
  Update index.d.ts (#8293)
  remove charset from jsloader script set (#8346)
  Log resolved targets in verbose log level (#8254)
  Fix missing module in large app from Experimental Bundler (#8303)
  [Symbol Propagation] Non-deterministic bundle hashes (#8212)
  ...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants